﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Net;

namespace MyVoteMVC.Common.Objects
{
    public class ESmtpClientArgs:EventArgs
    {
        public string message { get; set; }
        public object Tag { get; set; }
    }

    public class ESmtpClient:ICloneable
    {
        public event EventHandler<ESmtpClientArgs> SendCompleted;

        public string Host { get; set; }
        public int Port { get; set; }
        public bool EnableSsl { get; set; }
        public bool EnableAuth { get; set; }
        public string Name { get; set; }
        public string Password { get; set; }

        private TcpClient client = null;
        private Stream stream = null;
        public int Timeout { get; set; }

      

        public void SendAsync(MailMessage message)
        {
            Thread t = new Thread(new ParameterizedThreadStart(SendMail));
            t.IsBackground = true;
            t.Start(message);
        }

        public void SendMail(object msg)
        {
            string msgString = "";

            //Thread.Sleep(4000);
            //if (string.IsNullOrEmpty(msgString))
            //{
            //    msgString = "发送成功";
            //}
            //RaiseSendCompleted(msg as MailMessage, msgString);
            //return;

            TcpClient client = new TcpClient();
            Stream stream = null;
            if (Timeout != 0)
            {
                client.ReceiveTimeout = client.SendTimeout = Timeout;
            }

            try
            {
                MailMessage message = msg as MailMessage;

                client.Connect(Host, Port);

                if (EnableSsl)
                {
                    //ssl
                    stream = new SslStream(client.GetStream(), true, new RemoteCertificateValidationCallback(ValidateServerCertificate));
                    try
                    {
                        ((SslStream)stream).AuthenticateAsClient(Host, null, System.Security.Authentication.SslProtocols.Tls, false);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
                }
                else
                {
                    stream = client.GetStream();
                }

                //if ssl go esmtp then go smtp
                string code = "";
                code = MailMethods.ReaderCode(stream);

                if (code != MailCode.READY)
                {
                    throw new SendSmtpException(code);
                }

                //say hi
                if (EnableAuth)
                {
                    MailMethods.SendCommand(SmtpCommand.GetCommand("server", SmtpCommandType.EHELLO), stream);
                }
                else
                {
                    MailMethods.SendCommand(SmtpCommand.GetCommand("server", SmtpCommandType.HELLO), stream);
                }
                code = MailMethods.ReaderCode(stream);
                if (code != MailCode.OK)
                {
                    throw new SendSmtpException(code);
                }

                //login
                if (EnableAuth)
                {
                    //auth command
                    MailMethods.SendCommand(SmtpCommand.GetCommand(SmtpCommandType.AUTH_LOGIN), stream);
                    code = MailMethods.ReaderCode(stream);
                    if (code != MailCode.AUTH)
                    {
                        throw new SendSmtpException(code);
                    }

                    //username
                    MailMethods.SendCommand(Convert.ToBase64String(Encoding.Default.GetBytes(Name)), stream);
                    code = MailMethods.ReaderCode(stream);
                    if (code != MailCode.AUTH)
                    {
                        throw new SendSmtpException(code);
                    }

                    //psw
                    MailMethods.SendCommand(Convert.ToBase64String(Encoding.Default.GetBytes(Password)), stream);
                    code = MailMethods.ReaderCode(stream);
                    if (code != MailCode.AUTH_OK)
                    {
                        throw new SendSmtpException(code);
                    }
                }

                MailMethods.SendCommand(SmtpCommand.GetCommand(message.From, SmtpCommandType.MAIL_FROM), stream);
                code = MailMethods.ReaderCode(stream);
                if (code != MailCode.OK)
                {
                    throw new SendSmtpException(code);
                }

                foreach (string To in message.Tos)
                {
                    MailMethods.SendCommand(SmtpCommand.GetCommand(To, SmtpCommandType.RCPT_TO), stream);
                    code = MailMethods.ReaderCode(stream);
                    if (code != MailCode.OK)
                    {
                        throw new SendSmtpException(code,To);
                    }
                }

                //send body
                MailMethods.SendCommand(SmtpCommand.GetCommand(SmtpCommandType.DATA), stream);
                code = MailMethods.ReaderCode(stream);
                if (code != MailCode.DATA_READY)
                {
                    throw new SendSmtpException(code);
                }
                if (string.IsNullOrWhiteSpace(message.Subject))
                {
                    throw new SendSmtpException("999");
                }
                MailMethods.SendHeader(stream, message);
                MailMethods.SendBody(message.Body, stream, message.Encoding, message.IsBodyHtml);
               // MailMethods.SendAttchments(message.Attachment, stream, message.Encoding);
                MailMethods.SendAttchments(message.AttachmentData, stream, message.Encoding);
                MailMethods.WriterEnd(stream, message.Encoding);
                code = MailMethods.ReaderCode(stream);
                if (code != MailCode.OK)
                {
                    throw new SendSmtpException(code);
                }
                MailMethods.SendCommand(SmtpCommand.GetCommand(SmtpCommandType.QUIT), stream);
                
            }
            catch (SendSmtpException ex)
            {
                msgString = string.Format("{0}", ex.SmtpMessage);
            }
            catch (Exception ex)
            {
                msgString = string.Format("{0}", ex.Message);
                //throw new SendSmtpException(string.Format("无法连接服务器,{0}", ex));
            }
            finally
            {
                CloseConnection(client, stream);
                if (string.IsNullOrEmpty(msgString))
                {
                    msgString = "发送成功";
                }
                RaiseSendCompleted(msg as MailMessage, msgString);
            }
        }

        public void RaiseSendCompleted(MailMessage mailMessage,string message)
        {
            if (SendCompleted != null)
            {
                ESmtpClientArgs eventArgs = new ESmtpClientArgs();
                eventArgs.message = message;
                eventArgs.Tag = mailMessage.Tag;
                SendCompleted(this, eventArgs);
            }
        }


        /// <summary>
        /// SSL 远程回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="certificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        public bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {

            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;
            return false;
        }

        private void CloseConnection(TcpClient client, Stream stream)
        {
            try
            {
                stream.Close();
                client.Close();
            }
            catch
            {

            }
        }

        private void CloseConnection()
        {
            try
            {
                stream.Close();
                client.Close();
            }
            catch
            {

            }
        }


        #region ICloneable Members

        public object Clone()
        {
            return new ESmtpClient()
            {
                EnableAuth = this.EnableAuth,
                EnableSsl = this.EnableSsl,
                Host = this.Host,
                Name = this.Name,
                Password = this.Password,
                Port = this.Port,
                Timeout = this.Timeout,
            };
        }

        #endregion
    }
}
